<html><head><meta charset="utf-8"/><title>fs</title></head><body><h1>fs</h1><div class="x-wiki-content x-main-content">
 <p>
  Node.js内置的
  <code>
   fs
  </code>
  模块就是文件系统模块，负责读写文件。
 </p>
 <p>
  和所有其它JavaScript模块不同的是，
  <code>
   fs
  </code>
  模块同时提供了异步和同步的方法。
 </p>
 <p>
  回顾一下什么是异步方法。因为JavaScript的单线程模型，执行IO操作时，JavaScript代码无需等待，而是传入回调函数后，继续执行后续JavaScript代码。比如jQuery提供的
  <code>
   getJSON()
  </code>
  操作：
 </p>
 <pre><code>$.getJSON('http://example.com/ajax', function (data) {
    console.log('IO结果返回后执行...');
});
console.log('不等待IO结果直接执行后续代码...');
</code></pre>
 <p>
  而同步的IO操作则需要等待函数返回：
 </p>
 <pre><code>// 根据网络耗时，函数将执行几十毫秒~几秒不等:
var data = getJSONSync('http://example.com/ajax');
</code></pre>
 <p>
  同步操作的好处是代码简单，缺点是程序将等待IO操作，在等待时间内，无法响应其它任何事件。而异步读取不用等待IO操作，但代码较麻烦。
 </p>
 <h3>
  异步读文件
 </h3>
 <p>
  按照JavaScript的标准，异步读取一个文本文件的代码如下：
 </p>
 <pre><code>'use strict';

var fs = require('fs');

fs.readFile('sample.txt', 'utf-8', function (err, data) {
    if (err) {
        console.log(err);
    } else {
        console.log(data);
    }
});
</code></pre>
 <p>
  请注意，
  <code>
   sample.txt
  </code>
  文件必须在当前目录下，且文件编码为
  <code>
   utf-8
  </code>
  。
 </p>
 <p>
  异步读取时，传入的回调函数接收两个参数，当正常读取时，
  <code>
   err
  </code>
  参数为
  <code>
   null
  </code>
  ，
  <code>
   data
  </code>
  参数为读取到的String。当读取发生错误时，
  <code>
   err
  </code>
  参数代表一个错误对象，
  <code>
   data
  </code>
  为
  <code>
   undefined
  </code>
  。这也是Node.js标准的回调函数：第一个参数代表错误信息，第二个参数代表结果。后面我们还会经常编写这种回调函数。
 </p>
 <p>
  由于
  <code>
   err
  </code>
  是否为
  <code>
   null
  </code>
  就是判断是否出错的标志，所以通常的判断逻辑总是：
 </p>
 <pre><code>if (err) {
    // 出错了
} else {
    // 正常
}
</code></pre>
 <p>
  如果我们要读取的文件不是文本文件，而是二进制文件，怎么办？
 </p>
 <p>
  下面的例子演示了如何读取一个图片文件：
 </p>
 <pre><code>'use strict';

var fs = require('fs');

fs.readFile('sample.png', function (err, data) {
    if (err) {
        console.log(err);
    } else {
        console.log(data);
        console.log(data.length + ' bytes');
    }
});
</code></pre>
 <p>
  当读取二进制文件时，不传入文件编码时，回调函数的
  <code>
   data
  </code>
  参数将返回一个
  <code>
   Buffer
  </code>
  对象。在Node.js中，
  <code>
   Buffer
  </code>
  对象就是一个包含零个或任意个字节的数组（注意和Array不同）。
 </p>
 <p>
  <code>
   Buffer
  </code>
  对象可以和String作转换，例如，把一个
  <code>
   Buffer
  </code>
  对象转换成String：
 </p>
 <pre><code>// Buffer -&gt; String
var text = data.toString('utf-8');
console.log(text);
</code></pre>
 <p>
  或者把一个String转换成
  <code>
   Buffer
  </code>
  ：
 </p>
 <pre><code>// String -&gt; Buffer
var buf = Buffer.from(text, 'utf-8');
console.log(buf);
</code></pre>
 <h3>
  同步读文件
 </h3>
 <p>
  除了标准的异步读取模式外，
  <code>
   fs
  </code>
  也提供相应的同步读取函数。同步读取的函数和异步函数相比，多了一个
  <code>
   Sync
  </code>
  后缀，并且不接收回调函数，函数直接返回结果。
 </p>
 <p>
  用
  <code>
   fs
  </code>
  模块同步读取一个文本文件的代码如下：
 </p>
 <pre><code>'use strict';

var fs = require('fs');

var data = fs.readFileSync('sample.txt', 'utf-8');
console.log(data);
</code></pre>
 <p>
  可见，原异步调用的回调函数的
  <code>
   data
  </code>
  被函数直接返回，函数名需要改为
  <code>
   readFileSync
  </code>
  ，其它参数不变。
 </p>
 <p>
  如果同步读取文件发生错误，则需要用
  <code>
   try...catch
  </code>
  捕获该错误：
 </p>
 <pre><code>try {
    var data = fs.readFileSync('sample.txt', 'utf-8');
    console.log(data);
} catch (err) {
    // 出错了
}
</code></pre>
 <h3>
  写文件
 </h3>
 <p>
  将数据写入文件是通过
  <code>
   fs.writeFile()
  </code>
  实现的：
 </p>
 <pre><code>'use strict';

var fs = require('fs');

var data = 'Hello, Node.js';
fs.writeFile('output.txt', data, function (err) {
    if (err) {
        console.log(err);
    } else {
        console.log('ok.');
    }
});
</code></pre>
 <p>
  <code>
   writeFile()
  </code>
  的参数依次为文件名、数据和回调函数。如果传入的数据是String，默认按UTF-8编码写入文本文件，如果传入的参数是
  <code>
   Buffer
  </code>
  ，则写入的是二进制文件。回调函数由于只关心成功与否，因此只需要一个
  <code>
   err
  </code>
  参数。
 </p>
 <p>
  和
  <code>
   readFile()
  </code>
  类似，
  <code>
   writeFile()
  </code>
  也有一个同步方法，叫
  <code>
   writeFileSync()
  </code>
  ：
 </p>
 <pre><code>'use strict';

var fs = require('fs');

var data = 'Hello, Node.js';
fs.writeFileSync('output.txt', data);
</code></pre>
 <h3>
  stat
 </h3>
 <p>
  如果我们要获取文件大小，创建时间等信息，可以使用
  <code>
   fs.stat()
  </code>
  ，它返回一个
  <code>
   Stat
  </code>
  对象，能告诉我们文件或目录的详细信息：
 </p>
 <pre><code>'use strict';

var fs = require('fs');

fs.stat('sample.txt', function (err, stat) {
    if (err) {
        console.log(err);
    } else {
        // 是否是文件:
        console.log('isFile: ' + stat.isFile());
        // 是否是目录:
        console.log('isDirectory: ' + stat.isDirectory());
        if (stat.isFile()) {
            // 文件大小:
            console.log('size: ' + stat.size);
            // 创建时间, Date对象:
            console.log('birth time: ' + stat.birthtime);
            // 修改时间, Date对象:
            console.log('modified time: ' + stat.mtime);
        }
    }
});
</code></pre>
 <p>
  运行结果如下：
 </p>
 <pre><code>isFile: true
isDirectory: false
size: 181
birth time: Fri Dec 11 2015 09:43:41 GMT+0800 (CST)
modified time: Fri Dec 11 2015 12:09:00 GMT+0800 (CST)
</code></pre>
 <p>
  <code>
   stat()
  </code>
  也有一个对应的同步函数
  <code>
   statSync()
  </code>
  ，请试着改写上述异步代码为同步代码。
 </p>
 <h3>
  异步还是同步
 </h3>
 <p>
  在
  <code>
   fs
  </code>
  模块中，提供同步方法是为了方便使用。那我们到底是应该用异步方法还是同步方法呢？
 </p>
 <p>
  由于Node环境执行的JavaScript代码是服务器端代码，所以，绝大部分需要在服务器运行期反复执行业务逻辑的代码，
  <em>
   必须使用异步代码
  </em>
  ，否则，同步代码在执行时期，服务器将停止响应，因为JavaScript只有一个执行线程。
 </p>
 <p>
  服务器启动时如果需要读取配置文件，或者结束时需要写入到状态文件时，可以使用同步代码，因为这些代码只在启动和结束时执行一次，不影响服务器正常运行时的异步执行。
 </p>
 <h3>
  参考源码
 </h3>
 <p>
  <a href="https://github.com/michaelliao/learn-javascript/tree/master/samples/node/fs">
   fs
  </a>
 </p>
</div>
</body></html>